﻿using System;
using System.Collections.Generic;
using System.Linq;
using CSLua;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.ILAst;
using Mono.Cecil;
using Mono.Cecil.Cil;

namespace TK_CSLua.ILAnalyze
{
	// Token: 0x02000006 RID: 6
	public class LuaMethod
	{
		// Token: 0x17000001 RID: 1
		// (get) Token: 0x06000009 RID: 9 RVA: 0x000022F9 File Offset: 0x000004F9
		// (set) Token: 0x0600000A RID: 10 RVA: 0x00002301 File Offset: 0x00000501
		public ILBlock ILMethodBlock { get; private set; }

		// Token: 0x17000002 RID: 2
		// (get) Token: 0x0600000B RID: 11 RVA: 0x0000230A File Offset: 0x0000050A
		// (set) Token: 0x0600000C RID: 12 RVA: 0x00002312 File Offset: 0x00000512
		public SrcFileCache SrcFile { get; private set; }

		// Token: 0x17000003 RID: 3
		// (get) Token: 0x0600000D RID: 13 RVA: 0x0000231B File Offset: 0x0000051B
		public IEnumerable<LuaMethodSegment> LuaSegments
		{
			get
			{
				return this.luaSegments;
			}
		}

		// Token: 0x17000004 RID: 4
		// (get) Token: 0x0600000E RID: 14 RVA: 0x00002323 File Offset: 0x00000523
		public IEnumerable<GenericParameter> GenericParameters
		{
			get
			{
				return this.genericParameters;
			}
		}

		// Token: 0x17000005 RID: 5
		// (get) Token: 0x0600000F RID: 15 RVA: 0x0000232B File Offset: 0x0000052B
		public IEnumerable<string> Args
		{
			get
			{
				return this.args;
			}
		}

		// Token: 0x17000006 RID: 6
		// (get) Token: 0x06000010 RID: 16 RVA: 0x00002333 File Offset: 0x00000533
		public MethodDefinition method
		{
			get
			{
				return this.ILMethod;
			}
		}

		// Token: 0x17000007 RID: 7
		// (get) Token: 0x06000011 RID: 17 RVA: 0x0000233B File Offset: 0x0000053B
		// (set) Token: 0x06000012 RID: 18 RVA: 0x00002343 File Offset: 0x00000543
		public bool IsIEnumerator { get; private set; }

		// Token: 0x06000013 RID: 19 RVA: 0x0000234C File Offset: 0x0000054C
		public LuaMethod(LuaType luaType, MethodDefinition ilMethod)
		{
			this.LuaType = luaType;
			this.ILMethod = ilMethod;
		}

		// Token: 0x06000014 RID: 20 RVA: 0x00002364 File Offset: 0x00000564
		private string generateNewLocalName()
		{
			string arg = "_CSL_GEN_";
			int num = this.generateLocalCounter;
			this.generateLocalCounter = num + 1;
			return this.addLocalValue(arg + num, false);
		}

		// Token: 0x06000015 RID: 21 RVA: 0x00002398 File Offset: 0x00000598
		private string addLocalValue(string fullName, bool isArg = false)
		{
			this._methodValueIndexDict.Add(fullName, this._methodValueIndexDict.Count);
			string luaName = this.resloveLocalName(fullName);
			if (isArg)
			{
				this.args.Add(luaName);
			}
			return luaName;
		}

		// Token: 0x06000016 RID: 22 RVA: 0x000023D4 File Offset: 0x000005D4
		private bool checkLocalValue(string fullName, out string luaName)
		{
			if (this._methodValueDict.TryGetValue(fullName, out luaName))
			{
				return true;
			}
			luaName = this.addLocalValue(fullName, false);
			return false;
		}

		// Token: 0x06000017 RID: 23 RVA: 0x000023F4 File Offset: 0x000005F4
		private string resloveLocalName(string fullName)
		{
			string name;
			if (!this._methodValueDict.TryGetValue(fullName, out name))
			{
				if (fullName == "...")
				{
					name = "...";
					if (this._methodValueDict.Values.Contains(name))
					{
						throw new Exception("函数体里存在多个多变量");
					}
				}
				else
				{
					name = BuilderUtils.toIdentifier(fullName);
					int i = 0;
					while (this._methodValueDict.Values.Contains(name))
					{
						name = BuilderUtils.toIdentifier(fullName) + "_" + i++;
					}
				}
				this._methodValueDict.Add(fullName, name);
			}
			return name;
		}

		// Token: 0x06000018 RID: 24 RVA: 0x00002489 File Offset: 0x00000689
		public string getMethodValueLocalName(string fullName)
		{
			return this._methodValueDict[fullName];
		}

		// Token: 0x06000019 RID: 25 RVA: 0x00002497 File Offset: 0x00000697
		public int getMethodValueLocalIndex(string fullName)
		{
			return this._methodValueIndexDict[fullName];
		}

		// Token: 0x0600001A RID: 26 RVA: 0x000024A8 File Offset: 0x000006A8
		private void analyzeParameter()
		{
			this._methodValueDict = new Dictionary<string, string>();
			this._methodValueIndexDict = new Dictionary<string, int>();
			this.args = new List<string>();
			this.genericParameters = new List<GenericParameter>();
			if (!this.method.IsStatic)
			{
				this.addLocalValue("self", false);
			}
			if (this.ILMethod.getAttribute(BuildConfig.genericParamAttribute) != null)
			{
				foreach (GenericParameter typeArg in this.ILMethod.GenericParameters)
				{
					this.addLocalValue(typeArg.Name, true);
				}
			}
			foreach (ParameterDefinition arg in this.ILMethod.Parameters)
			{
				this.addLocalValue(arg.luaLocalFullName(), true);
			}
		}

		// Token: 0x0600001B RID: 27 RVA: 0x000025B0 File Offset: 0x000007B0
		private void analyzeNodeParent(ILNode node)
		{
			if (this._parentDict == null)
			{
				this._parentDict = new Dictionary<ILNode, ILNode>();
			}
			foreach (ILNode child in node.GetChildren())
			{
				this._parentDict.Add(child, node);
				this.analyzeNodeParent(child);
			}
		}

		// Token: 0x0600001C RID: 28 RVA: 0x00002620 File Offset: 0x00000820
		public ILNode GetParent(ILNode node)
		{
			return this._parentDict[node];
		}

		// Token: 0x0600001D RID: 29 RVA: 0x00002630 File Offset: 0x00000830
		private void analyzeExtractSegment(ILBlock block)
		{
			List<ILNode> extract = new List<ILNode>();
			for (int i = 0; i < block.Body.Count; i++)
			{
				extract.Clear();
				ILNode segment = block.Body[i];
				this.analyzeExtractSegmentRecu(segment, extract);
				for (int j = 0; j < extract.Count; j++)
				{
					block.Body.Insert(i + j, extract[j]);
				}
				i += extract.Count;
			}
		}

		// Token: 0x0600001E RID: 30 RVA: 0x000026A4 File Offset: 0x000008A4
		private void analyzeExtractSegmentRecu(ILNode node, List<ILNode> extract)
		{
			if (node is ILBlock)
			{
				this.analyzeExtractSegment(node as ILBlock);
				return;
			}
			if (node is ILExpression)
			{
				ILExpression exp = node as ILExpression;
				for (int i = 0; i < exp.Arguments.Count; i++)
				{
					ILExpression argExp = exp.Arguments[i];
					this.analyzeExtractSegmentRecu(argExp, extract);
					if (argExp.ExpectedType != null && argExp.Code == ILCode.Stloc)
					{
						ILExpression turnExp = this.generateExtractExpression(argExp, ILCode.Ldloc);
						argExp.ExpectedType = null;
						turnExp.Arguments.RemoveAt(0);
						exp.Arguments[i] = turnExp;
						extract.Add(argExp);
					}
					if (argExp.ExpectedType != null && argExp.Code == ILCode.Stsfld)
					{
						ILExpression turnExp2 = this.generateExtractExpression(argExp, ILCode.Ldsfld);
						argExp.ExpectedType = null;
						turnExp2.Arguments.RemoveAt(0);
						exp.Arguments[i] = turnExp2;
						extract.Add(argExp);
					}
					if (argExp.ExpectedType != null && argExp.Code == ILCode.Stfld)
					{
						ILExpression turnExp3 = this.generateExtractExpression(argExp, ILCode.Ldfld);
						argExp.ExpectedType = null;
						turnExp3.Arguments.RemoveAt(1);
						exp.Arguments[i] = turnExp3;
						extract.Add(argExp);
					}
				}
				return;
			}
			foreach (ILNode child in node.GetChildren())
			{
				if (node != null)
				{
					this.analyzeExtractSegmentRecu(child, extract);
				}
			}
		}

		// Token: 0x0600001F RID: 31 RVA: 0x0000282C File Offset: 0x00000A2C
		private ILExpression generateExtractExpression(ILExpression argExp, ILCode code)
		{
			ILExpression ilexpression = LuaMethod.CloneExpression(argExp);
			ilexpression.Code = code;
			return ilexpression;
		}

		// Token: 0x06000020 RID: 32 RVA: 0x0000283C File Offset: 0x00000A3C
		public static ILExpression CloneExpression(ILExpression argExp)
		{
			ILExpression turnExp = new ILExpression(argExp.Code, argExp.Operand, new ILExpression[0]);
			turnExp.ExpectedType = argExp.ExpectedType;
			turnExp.InferredType = argExp.InferredType;
			if (argExp.Prefixes != null)
			{
				foreach (ILExpressionPrefix p in argExp.Prefixes)
				{
					turnExp.AddPrefix(p);
				}
			}
			turnExp.Arguments = new List<ILExpression>();
			if (argExp.Arguments != null)
			{
				foreach (ILExpression arg in argExp.Arguments)
				{
					turnExp.Arguments.Add(LuaMethod.CloneExpression(arg));
				}
			}
			return turnExp;
		}

		// Token: 0x06000021 RID: 33 RVA: 0x00002908 File Offset: 0x00000B08
		private void analyzeBody()
		{
			DecompilerContext context = new DecompilerContext(this.ILMethod.Module)
			{
				CurrentType = this.ILMethod.DeclaringType,
				CurrentMethod = this.ILMethod
			};
			ILBlock ilMethod = new ILBlock(new ILNode[0]);
			ILAstBuilder astBuilder = new ILAstBuilder();
			ilMethod.Body = astBuilder.Build(this.ILMethod, false, context);
			context.CancellationToken.ThrowIfCancellationRequested();
			new ILAstOptimizer().Optimize(context, ilMethod, ILAstOptimizationStep.None);
			this.ILMethodBlock = ilMethod;
			this.IsIEnumerator = this.ILMethodBlock.GetSelfAndChildrenRecursive<ILExpression>((ILExpression exp) => exp.Code == ILCode.YieldReturn || exp.Code == ILCode.YieldBreak).Any<ILExpression>();
			this.analyzeExtractSegment(ilMethod);
			this.luaSegments = new List<LuaMethodSegment>();
			foreach (ILNode segment in this.ILMethodBlock.Body)
			{
				LuaMethodSegment luaSegment = new LuaMethodSegment();
				luaSegment.ilSegment = segment;
				foreach (ILExpression exp2 in segment.GetSelfAndChildrenRecursive<ILExpression>(null))
				{
					if (exp2.Code == ILCode.Stloc || exp2.Code == ILCode.Ldloca)
					{
						ILVariable ilvar = exp2.Operand as ILVariable;
						string methodValueId;
						if (!this.checkLocalValue(ilvar.luaLocalFullName(), out methodValueId))
						{
							luaSegment.newLocalValues.Add(methodValueId);
						}
						luaSegment.localValues.Add(methodValueId);
					}
					if (exp2.Code == ILCode.Stloc)
					{
						TypeReference expectedType = exp2.ExpectedType;
					}
				}
				this.luaSegments.Add(luaSegment);
			}
		}

		// Token: 0x06000022 RID: 34 RVA: 0x00002AE8 File Offset: 0x00000CE8
		internal List<string> getExpressionComment(ILExpression exp)
		{
			int startLine = this.getExpressionStartLine(exp);
			if (startLine > 0)
			{
				return this.SrcFile.getComman(startLine);
			}
			return null;
		}

		// Token: 0x06000023 RID: 35 RVA: 0x00002B0F File Offset: 0x00000D0F
		public string getLocalValueName(ILVariable var)
		{
			return this.getMethodValueLocalName(var.luaLocalFullName());
		}

		// Token: 0x06000024 RID: 36 RVA: 0x00002B1D File Offset: 0x00000D1D
		public int getLocalValueIndex(ILVariable var)
		{
			return this.getMethodValueLocalIndex(var.luaLocalFullName());
		}

		// Token: 0x06000025 RID: 37 RVA: 0x00002B2C File Offset: 0x00000D2C
		public string getLocalValueName(ILExpression exp)
		{
			AnalyzeUtils.Assert(exp.Code == ILCode.Stloc || exp.Code == ILCode.Ldloc || exp.Code == ILCode.Ldloca, null);
			ILVariable ilvar = exp.Operand as ILVariable;
			if (ilvar != null)
			{
				return this.getLocalValueName(ilvar);
			}
			throw new Exception();
		}

		// Token: 0x06000026 RID: 38 RVA: 0x00002B88 File Offset: 0x00000D88
		public int getLocalValueIndex(ILExpression exp)
		{
			AnalyzeUtils.Assert(exp.Code == ILCode.Stloc || exp.Code == ILCode.Ldloc || exp.Code == ILCode.Ldloca, null);
			ILVariable ilvar = exp.Operand as ILVariable;
			if (ilvar != null)
			{
				return this.getLocalValueIndex(ilvar);
			}
			throw new Exception();
		}

		// Token: 0x06000027 RID: 39 RVA: 0x00002BE4 File Offset: 0x00000DE4
		private void analyzeSource()
		{
			this._instructionMap = new Dictionary<int, Instruction>();
			Instruction firstDocedInstruction = this.ILMethod.Body.Instructions.FirstOrDefault((Instruction x) => x.SequencePoint != null);
			if (firstDocedInstruction != null)
			{
				Document doc = firstDocedInstruction.SequencePoint.Document;
				if (doc != null)
				{
					this.SrcFile = SrcFileCache.getSrc(doc.Url);
				}
			}
			foreach (Instruction ins in this.ILMethod.Body.Instructions)
			{
				this._instructionMap.Add(ins.Offset, ins);
			}
		}

		// Token: 0x06000028 RID: 40 RVA: 0x00002CB0 File Offset: 0x00000EB0
		internal void DoAnalyze()
		{
			this.analyzeSource();
			this.analyzeParameter();
			this.analyzeBody();
			this.analyzeNodeParent(this.ILMethodBlock);
		}

		// Token: 0x06000029 RID: 41 RVA: 0x00002CD0 File Offset: 0x00000ED0
		internal int getExpressionStartLine(ILNode expression)
		{
			if (this.SrcFile != null)
			{
				int startLine = -1;
				foreach (ILExpression subExp in expression.GetSelfAndChildrenRecursive<ILExpression>(null))
				{
					if (subExp.ILRanges.Any<ILRange>())
					{
						ILRange range = subExp.ILRanges.FirstOrDefault<ILRange>();
						Instruction inst = this._instructionMap[range.From];
						if (inst != null && inst.SequencePoint != null)
						{
							startLine = inst.SequencePoint.StartLine;
						}
					}
				}
				return startLine;
			}
			return -1;
		}

		// Token: 0x04000004 RID: 4
		public readonly LuaType LuaType;

		// Token: 0x04000005 RID: 5
		public readonly MethodDefinition ILMethod;

		// Token: 0x04000008 RID: 8
		private List<LuaMethodSegment> luaSegments;

		// Token: 0x04000009 RID: 9
		private List<GenericParameter> genericParameters;

		// Token: 0x0400000A RID: 10
		private List<string> args;

		// Token: 0x0400000B RID: 11
		private Dictionary<int, Instruction> _instructionMap;

		// Token: 0x0400000C RID: 12
		private Dictionary<string, string> _methodValueDict;

		// Token: 0x0400000D RID: 13
		private Dictionary<string, int> _methodValueIndexDict;

		// Token: 0x0400000E RID: 14
		private Dictionary<ILNode, ILNode> _parentDict;

		// Token: 0x0400000F RID: 15
		private int generateLocalCounter;
	}
}
